import os
import logging
import logging.config
import json
import threading
import time
import signal
import urllib
import urllib2
import ConfigParser
import platform
import hashlib
import socket
import random
import re
import subprocess

BASEDIR = os.path.split(os.path.realpath(__file__))[0]
HOMEDIR = os.path.join(BASEDIR, '..')

base = socket.gethostname() + 'mondo' + str(time.time()) + str(random.random())
UUID = hashlib.md5(base).hexdigest().upper()

os.chdir(HOMEDIR)

DIRS = dict({
    'conf': os.path.join(BASEDIR, '../conf/'),
    'shell': os.path.join(BASEDIR, '../sh/'),
    'python': os.path.join(BASEDIR, '../py/'),
    'bin': os.path.join(BASEDIR, '../bin/'),
    'flag': os.path.join(BASEDIR, '../run/')
})

map(os.makedirs, [d for d in DIRS.values() if not os.path.exists(d)])


def checkprocess(regx, user=None, pidfile=None, pid=None):
    return len(getprocess(regx, user, pidfile, pid)) == 0


def getprocess(regx, user=None, pidfile=None, pid=None):
    p = subprocess.Popen(
        ['ps', 'aux', '--width', '3000'], stdout=subprocess.PIPE)
    stdout, stderr = p.communicate()
    lines = stdout.split('\n')
    if len(lines) <= 1:
        raise Exception('PS_ERROR')
    return check_process(lines, regx, user, pidfile, pid)


def check_process_by_pid(pid):
    if type(pid) == str:
        pid = int(pid)
    if type(pid) != int:
        raise Exception('PID_ERROR')
    return checkprocess('.*', None, None, str(pid))


def stop_processes_by_pattern(pattern):
    pids = getprocess(pattern)
    unkilled = [pid for pid in pids if stop_proc(pid)]
    if len(unkilled) > 0:
        raise Exception('kill %s error' % str(unkilled))
    return 'killed %s' % str(pids)


def stop_proc(proc, timeout=5):
    if type(proc) == subprocess.Popen:
        pid = proc.pid
    if type(proc) == str:
        pid = int(proc)
    if pid:
        os.kill(pid, signal.SIGTERM)
        count = timeout
        while count > 0:
            if not check_process_by_pid(pid):
                break
            count -= 1
        if check_process_by_pid(pid):
            os.kill(pid, signal.SIGKILL)
            time.sleep(timeout)
        return not check_process_by_pid(pid)


def byteify(input):
    if isinstance(input, dict):
        ret = {}
        for key, value in input.iteritems():
            ret[byteify(key)] = byteify(value)
        return ret
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input


def timed_start(start_script, timeout):
    if len(start_script) == 0:
        return
    new_start_script = start_script[0]
    if not new_start_script.startswith('/'):
        new_start_script = os.path.join(DIRS['shell'], new_start_script)
    proc = subprocess.Popen([new_start_script] + list(start_script[1:]))
    count = timeout
    return_code = proc.poll()
    while return_code is None and count > 0:
        return_code = proc.poll()
        count -= 1
        time.sleep(1)
    if return_code is None:
        stop_proc(proc)
    return return_code


def check_process(lines, regx, user='mondev', pidfile=None, pid=None):
    pattern = re.compile(regx)
    space_pattern = re.compile(
        '''(?P<user>(\S+))(\s+)(?P<pid>\d+)(\s+)(\d+\.\d+(e-\d+)?)(\s+)(\d+\.\d+(e-\d+)?)(\s+)(\d+)(\s+)(\d+)(\s+)(\S+)(\s+)(\S+)(\s+)(\S+)(\s+)(\d+:
\d+)(\s+)(?P<command>.*$)'''
    )
    matched_pids = []
    for line in lines[1:]:
        m = space_pattern.match(line)
        if m:
            matched = False
            data = m.groupdict()
            if pattern.match(data['command']):
                matched = True
                if pidfile and pidfile != "":
                    pid = file(pidfile, 'r').read()
                if pid and pid != "":
                    matched = (matched and data['pid'] == pid)
                if user and user != "":
                    matched = (matched and data['user'] == user)
                if matched:
                    matched_pids.append(data['pid'])
    return matched_pids


TASK_CONF_FILE = os.path.join(DIRS['conf'], 'task.conf')
BASE_CONF_FILE = os.path.join(DIRS['conf'], 'magent.conf')
LOG_CONF_FILE = os.path.join(DIRS['conf'], 'log.conf')
PID_FILE = os.path.join(DIRS['flag'], 'magent.pid')

BASE_CONF = {}
with open(BASE_CONF_FILE) as f:
    BASE_CONF = json.load(f)
BASEURL = BASE_CONF.get('server_url', None)

try:
    logging.config.fileConfig(LOG_CONF_FILE)
except ConfigParser.NoSectionError, ex:
    print '%s format error %s' % LOG_CONF_FILE, str(ex)


def version_check():
    v = platform.python_version()
    return float(v[:v.rfind('.')]) >= 2.6


def daemonize():
    pid = str(os.getpid())
    file(PID_FILE, 'w+').write(pid)


def getlogger(name):
    return logging.getLogger(name)


def http_err_log(err, job):
    if isinstance(err, urllib2.HTTPError):  # 'CON_ERR'
        pass
    if isinstance(err, urllib2.URLError):  # 'SERVER_ERR'
        pass
    if isinstance(err, socket.timeout):  # 'TIMEOUT_ERR'
        pass
    if isinstance(err, socket.error):  # 'SOCKET_ERR'
        pass
    else:
        pass


def restart_process(start_args):
    if len(start_args) < 1:
        return None
    start_script = start_args[0]
    if not start_script.startswith('/'):
        start_script = os.path.join(BASEDIR, '../sh/', start_script)
    return timed_start([start_script] + list(start_args[1:]), 5)


# def error_just_print_callback(err, job):
# def just_print_callback(result, job):

CALLBACKS = {}
ERR_CALLBACKS = dict({'notice_http_err': http_err_log})
